package com.m4f.cityclient.api.service.impl;

import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.consumer.OAuth2AccessTokenRequiredException;
import org.springframework.security.oauth2.consumer.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.consumer.OAuth2RestTemplate;
import org.springframework.security.oauth2.consumer.OAuth2SecurityContext;
import org.springframework.security.oauth2.consumer.OAuth2SecurityContextHolder;
import org.springframework.security.oauth2.consumer.OAuth2SecurityContextImpl;
import org.springframework.security.oauth2.consumer.token.OAuth2ClientTokenServices;

import com.google.api.client.json.GenericJson;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.JsonParser;
import com.google.api.client.json.jackson.JacksonFactory;
import com.m4f.cityclient.api.exception.M4FCityClientException;
import com.m4f.gaeweb.web.utils.StackTraceUtil;

public abstract class M4FBaseApiService {
	private static final Logger LOGGER = Logger.getLogger(M4FBaseApiService.class.getName());
	
	protected OAuth2RestTemplate m4fRestTemplate;
	protected OAuth2ClientTokenServices tokenServices;
	
	protected <T> T getData(String url, Class<T> clazz) throws M4FCityClientException {
		try {
			byte[] data = getM4fRestTemplate().getForObject(URI.create(url), byte[].class);
			JsonFactory jsonFactory = new JacksonFactory();
			JsonParser parser = jsonFactory.createJsonParser(new String(data));
			parser.nextToken();

			T t = parser.parseAndClose(clazz, null);
			if(t instanceof GenericJson) {
				GenericJson object = (GenericJson)t;
				Number errorCode = (Number) object.unknownFields.get("city_errorCode");
				String errorMessage = (String) object.unknownFields.get("city_errorMessage");
				String errorDescription = (String) object.unknownFields.get("city_errorDescription");
				if(this.isAnError(object)) throw new M4FCityClientException(errorCode.intValue(), errorMessage, errorDescription);
			}
			
			return t;
			
		} catch(InvalidTokenException badToken) {
			//we've got a bad token, probably because it's expired.
			OAuth2ProtectedResourceDetails resource = getM4fRestTemplate().getResource();
			OAuth2SecurityContext context = OAuth2SecurityContextHolder.getContext();
			if (context != null) {
				// this one is kind of a hack for this application
		        // the problem is that the sparklr photos page doesn't remove the 'code=' request parameter.
				((OAuth2SecurityContextImpl)context).setVerificationCode(null);
			}
			//clear any stored access tokens...
			getTokenServices().removeToken(SecurityContextHolder.getContext().getAuthentication(), resource);
			//go get a new access token...
			throw new OAuth2AccessTokenRequiredException(resource);
		} catch(IOException e) {
			LOGGER.log(Level.SEVERE, StackTraceUtil.getStackTrace(e));
			throw new M4FCityClientException(e);
		}
	}
	
	protected <T> T getData(String url, Class<T> clazz, String[] uriVarNames, Object[] uriVarValues) throws M4FCityClientException {
		try {
			StringBuffer finalUrl = new StringBuffer(url);
			HashMap<String, Object> params = new HashMap<String, Object>();
			
			int position = 0; // To know the current position for the uriVarValues array.
			for(String varName : uriVarNames) {
				if(!finalUrl.toString().endsWith("/")) {
					finalUrl.append("/");
				}
				finalUrl.append("{").append(varName).append("}");
				params.put(varName, uriVarValues[position]);
				position++;
			}
			
			byte[] data = getM4fRestTemplate().getForObject(finalUrl.toString(), byte[].class, params);
			JsonFactory jsonFactory = new JacksonFactory();
			JsonParser parser = jsonFactory.createJsonParser(new String(data));
			parser.nextToken();

			T t = parser.parseAndClose(clazz, null);
						
			return t;
		} catch(InvalidTokenException badToken) {
			//we've got a bad token, probably because it's expired.
			OAuth2ProtectedResourceDetails resource = getM4fRestTemplate().getResource();
			OAuth2SecurityContext context = OAuth2SecurityContextHolder.getContext();
			if (context != null) {
				// this one is kind of a hack for this application
		        // the problem is that the sparklr photos page doesn't remove the 'code=' request parameter.
				((OAuth2SecurityContextImpl)context).setVerificationCode(null);
			}
			//clear any stored access tokens...
			getTokenServices().removeToken(SecurityContextHolder.getContext().getAuthentication(), resource);
			//go get a new access token...
			throw new OAuth2AccessTokenRequiredException(resource);
		} catch(IOException e) {
			LOGGER.log(Level.SEVERE, StackTraceUtil.getStackTrace(e));
			throw new M4FCityClientException(e);
		}
	}
	
	public OAuth2RestTemplate getM4fRestTemplate() {
		return m4fRestTemplate;
	}

	public void setM4fRestTemplate(OAuth2RestTemplate m4fRestTemplate) {
		this.m4fRestTemplate = m4fRestTemplate;
	}

	public OAuth2ClientTokenServices getTokenServices() {
		return tokenServices;
	}

	public void setTokenServices(OAuth2ClientTokenServices tokenServices) {
		this.tokenServices = tokenServices;
	}
	
	private boolean isAnError(GenericJson object) {
		if(object.containsKey("city_errorCode") || object.containsKey("city_errorMessage")) {
			return true;
		}
		return false;
	}
}
